#ifndef _NE_NODE_H_
#define _NE_NODE_H_

#include <mathematics/Matrix4.h>

namespace ne
{
    enum TransformSpace
    {
        // Transform is relative to the local space
        TS_LOCAL,
        // Transform is relative to the space of the parent node
        TS_PARENT,
        // Transform is relative to world space
        TS_WORLD
    };

    enum TransformAlignType
    {
        TAT_BOTTOM_LEFT,
        TAT_BOTTOM,
        TAT_BOTTOM_RIGHT,

        TAT_LEFT,
        TAT_CENTER,
        TAT_RIGHT,

        TAT_TOP_LEFT,
        TAT_TOP,
        TAT_TOP_RIGHT
    };

    class NEAPI Node
    {
        public:
            typedef std::set<Node*> NodeSet;

        public:
            Node();

            // Transform Space is TS_WORLD
            // The initial value for Direction is Vector3::NEGATIVE_UNIT_Z
            Vector3 getLeft() const { return _getDerivedOrientation() * Vector3::NEGATIVE_UNIT_X; }
            Vector3 getRight() const { return _getDerivedOrientation() * Vector3::UNIT_X; }
            Vector3 getDown() const { return _getDerivedOrientation() * Vector3::NEGATIVE_UNIT_Y; }
            Vector3 getUp() const { return _getDerivedOrientation() * Vector3::UNIT_Y; }
            Vector3 getFront() const { return _getDerivedOrientation() * Vector3::NEGATIVE_UNIT_Z; }
            Vector3 getBack() const { return _getDerivedOrientation() * Vector3::UNIT_Z; }
            Vector3 getDirection() const { return _getDerivedOrientation() * Vector3::NEGATIVE_UNIT_Z; }

            // The relative to its parent.
            virtual void setPosition(const Vector3 &v);
            const Vector3& getPosition(const TransformSpace ts=TS_PARENT) const;

            virtual void setOrientation(const Quaternion &q);
            const Quaternion& getOrientation(const TransformSpace ts=TS_PARENT) const;

            virtual void setScale(const Vector3 &v);
            const Vector3& getScale(const TransformSpace ts=TS_PARENT) const;

            // The Initial
            void setInitial();
            const Vector3& getInitialPosition() const;
            const Quaternion& getInitialOrientation() const;
            const Vector3& getInitialScale() const;

            virtual void translate(const Vector3 &v, const TransformSpace ts=TS_LOCAL);

            virtual void roll(const real a, const TransformSpace ts=TS_LOCAL);
            virtual void yaw(const real a, const TransformSpace ts=TS_LOCAL);
            virtual void pitch(const real a, const TransformSpace ts=TS_LOCAL);
            virtual void rotate(const Vector3 &axis, const real a, const TransformSpace ts=TS_LOCAL);
            virtual void rotate(const Quaternion &q, const TransformSpace ts=TS_LOCAL);

            virtual void scale(const Vector3 &v);

            Node* getParent() const;

            Node* getRoot() const;

            bool isRoot() const;

            bool isBranch() const;

            bool isLeaf() const;

            // I think Node can't create child
            //virtual Node* createChild(const std::string &name, const Vector3 &position = Vector3::ZERO, const Quaternion &orientation = Quaternion::IDENTITY, const Vector3 &scale=Vector3::ONE);

            virtual bool addChild(Node *child);

            virtual Node* removeChild(Node *child);

            virtual void removeAllChildren();

            const NodeSet& getChildren() const;

            size_t getChildrenCount() const;

            virtual Vector3 convertWorldToLocalVector(const Vector3 &v) const;
            virtual Vector3 convertLocalToWorldVector(const Vector3 &v) const;

            virtual Vector3 convertWorldToLocalPosition(const Vector3 &v) const;
            virtual Vector3 convertLocalToWorldPosition(const Vector3 &v) const;

            virtual Quaternion convertWorldToLocalOrientation(const Quaternion &q) const;
            virtual Quaternion convertLocalToWorldOrientation(const Quaternion &q) const;

            virtual void reset();
            virtual void resetPosition();
            virtual void resetOrientation();
            virtual void resetScale();

            virtual void update();

            virtual void needUpdate(const bool forceParentUpdate=false);

            virtual void requestUpdate(Node *child, const bool forceParentUpdate=false);

            virtual void cancelUpdate(Node *child);

            // Used for convert Local To World
            virtual const Matrix4& getTransformMatrix() const;
            // Used for convert World To Local
            virtual const Matrix4& getInverseTransformMatrix() const;

            virtual void copyTransform(const Node &node, const TransformSpace ts=TS_WORLD);

            virtual ~Node();

        protected:
            virtual void _setParent(Node *parent);

            // The world Derived
            virtual void _setDerivedPosition(const Vector3 &v);
            const Vector3& _getDerivedPosition() const;

            virtual void _setDerivedOrientation(const Quaternion &q);
            const Quaternion& _getDerivedOrientation() const;

            const Vector3& _getDerivedScale() const;

            virtual void _update(const bool updateChildren, const bool parentHasChanged=false);

            virtual void _updateFromParent(const bool parentHasChanged=false) const;

            virtual void _updateTransformMatrix() const;
            virtual void _updateInverseTransformMatrix() const;

        private:
            Node(const Node &);

            Node& operator = (const Node &);

        protected:
            // The initial for reset
            Vector3 mInitialPosition;
            Quaternion mInitialOrientation;
            Vector3 mInitialScale;

            // mPosition, mOrientation and mScale of the node
            // relative to it's parent.
            Vector3 mPosition;
            Quaternion mOrientation;
            Vector3 mScale;

            // The Derived are used for Children
            mutable Vector3 mDerivedPosition;
            mutable Quaternion mDerivedOrientation;
            mutable Vector3 mDerivedScale;
            mutable Matrix4 mTransformMatrix;
            mutable Matrix4 mInverseTransformMatrix;

            mutable bool mNeedUpdate;
            mutable bool mNeedAllChildrenUpdate;
            mutable bool mParentNotified;
            mutable bool mNeedTransformMatrixUpdate;
            mutable bool mNeedInverseTransformMatrixUpdate;

            Node *mpParent;
            Node *mpRoot;
            NodeSet mChildren;
            NodeSet mChildrenToUpdate;
    };

    typedef std::set<Node*> NodeSet;
    typedef NodeSet::iterator NodeSetIterator;
    typedef NodeSet::const_iterator NodeSetConstIterator;
}

#endif

